package com.hna.eking.AirlineServer.security;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.NonceExpiredException;
import org.springframework.util.Assert;

import com.hna.eking.AirlineServer.Platform.Airport.AirportController;

import javax.servlet.FilterChain;  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import java.io.IOException;  
import java.util.ArrayList;
import java.util.List;
import java.util.Map;  
  
/** 
 * token的校验 
 * 该类继承自BasicAuthenticationFilter，在doFilterInternal方法中， 
 * 从http头的Authorization 项读取token数据，然后用Jwts包提供的方法校验token的合法性。 
 * 如果校验通过，就认为这是一个取得授权的合法请求 
 * 较父类相比，增加了failureHandler用于处理认证失败的请求
 * @author Jzz on 2018/9/13. 
 */  
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
	
	Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
  
    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {  
        super(authenticationManager);  
    }  
    
    //这个属性父类没有
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
    
    public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
		Assert.notNull(failureHandler, "failureHandler cannot be null");
		this.failureHandler = failureHandler;
	}
  
    @Override  
    protected final void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
    		FilterChain chain) throws IOException, ServletException {  
//        if (header == null || !header.startsWith("Bearer ")) { 
//        	//这地方绝对不要放过，否则后续会从session里面找到UsernamePasswordAuthenticationToken对象
//        	AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException("token 为空");
//        	failureHandler.onAuthenticationFailure(request, response, exception);
//            return;  
//        }  
        UsernamePasswordAuthenticationToken authentication =  null;
        try {
        	authentication = getAuthentication(request);  
        } catch(JwtException failed){
        	SecurityContextHolder.clearContext();

    		if (logger.isDebugEnabled()) {
    			logger.debug("token认证失败: " + failed.getMessage());
    		}
//    		rememberMeServices.loginFail(request, response);
    		NonceExpiredException exception = null;
    		if (failed instanceof ExpiredJwtException) {
    			exception = new NonceExpiredException("token is expired");
    		} else {
    			exception = new NonceExpiredException("token认证失败: " + failed.getMessage()); //暂时先找这么个异常用着，可以自定义一个好一点
    		}
    		
    		failureHandler.onAuthenticationFailure(request, response, exception);
    		return;  
        }
  
        SecurityContextHolder.getContext().setAuthentication(authentication);  
        chain.doFilter(request, response);  
  
    }  
  
    /* 每次验证token重新生成一个authentication对象， 然后设置到SecurityContext中
    /* 在Token中包含足够多的信息，以便在后续请求中减少查询数据库的几率； */
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) throws JwtException{  
        String token = request.getHeader("Authorization");  
        if (token != null) {  
        	Map<String, Object> jwtClaims =
                    Jwts.parser().setSigningKey("MyJwtSecret").parseClaimsJws(token.replace("Bearer ", "")).getBody();
  
            if (jwtClaims != null && jwtClaims.size() > 0) {  
            	String id = (String) jwtClaims.get("id");
            	String username = (String) jwtClaims.get("username");
            	String uuid = (String) jwtClaims.get("uuid");
            	@SuppressWarnings("unchecked")
				List<String> auths = (List<String>) jwtClaims.get("authorities");
            	List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            	for (String auth : auths) {
            		authorities.add(new SimpleGrantedAuthority(auth));
            	}
        		DeviceUser deviceUser = new DeviceUser(id, username, 
       				 "", uuid, authorities, true, true, true);
        		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
        				deviceUser, deviceUser.getPassword(),
        				deviceUser.getAuthorities());
                return result;  
            }  else {
            	throw new JwtException("Token中找不到用户信息");
            }
        } 
        return null;
    }  
    
    
  
}  
